/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * 版权所有 2002-2019 原作者或作者
 *
 * 根据 Apache 许可证 2.0 版本授权
 * 除非遵守许可证，否则不得使用此文件
 * 您可以在以下网址获取许可证副本
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * 除非适用法律要求或书面同意，否则按"原样"分发软件
 * 没有任何明示或暗示的保证或条件
 * 请参阅许可证了解特定语言权限和限制
 */

package org.springframework.context.annotation;

import java.util.LinkedHashSet;
import java.util.Set;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionDefaults;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;

/**
 * A bean definition scanner that detects bean candidates on the classpath,
 * registering corresponding bean definitions with a given registry ({@code BeanFactory}
 * or {@code ApplicationContext}).
 *
 * <p>Candidate classes are detected through configurable type filters. The
 * default filters include classes that are annotated with Spring's
 * {@link org.springframework.stereotype.Component @Component},
 * {@link org.springframework.stereotype.Repository @Repository},
 * {@link org.springframework.stereotype.Service @Service}, or
 * {@link org.springframework.stereotype.Controller @Controller} stereotype.
 *
 * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
 * JSR-330's {@link javax.inject.Named} annotations, if available.
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 2.5
 * @see AnnotationConfigApplicationContext#scan
 * @see org.springframework.stereotype.Component
 * @see org.springframework.stereotype.Repository
 * @see org.springframework.stereotype.Service
 * @see org.springframework.stereotype.Controller
 */

/**
 * 一个在类路径上检测bean候选的扫描器，
 * 将相应的bean定义注册到给定的注册表中({@code BeanFactory}
 * 或 {@code ApplicationContext})。
 *
 * <p>候选类通过可配置的类型过滤器检测。默认过滤器包括
 * 使用Spring的{@link org.springframework.stereotype.Component @Component},
 * {@link org.springframework.stereotype.Repository @Repository},
 * {@link org.springframework.stereotype.Service @Service}, 或
 * {@link org.springframework.stereotype.Controller @Controller} 注解的类。
 *
 * <p>如果可用，还支持Java EE 6的{@link javax.annotation.ManagedBean}和
 * JSR-330的{@link javax.inject.Named}注解。
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 2.5
 * @see AnnotationConfigApplicationContext#scan
 * @see org.springframework.stereotype.Component
 * @see org.springframework.stereotype.Repository
 * @see org.springframework.stereotype.Service
 * @see org.springframework.stereotype.Controller
 */
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

	// Bean定义注册表，用于注册扫描到的bean定义
	private final BeanDefinitionRegistry registry;

	// Bean定义的默认配置
	private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();

	// 自动装配候选模式的匹配模式
	@Nullable
	private String[] autowireCandidatePatterns;

	// Bean名称生成器，默认使用注解bean名称生成器
	private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;

	// 作用域元数据解析器，默认使用注解作用域元数据解析器
	private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

	// 是否包含注解配置，默认为true
	private boolean includeAnnotationConfig = true;


	/**
	 * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory.
	 * @param registry the {@code BeanFactory} to load bean definitions into, in the form
	 * of a {@code BeanDefinitionRegistry}
	 */
	/**
	 * 为给定的bean工厂创建一个新的{@code ClassPathBeanDefinitionScanner}。
	 * @param registry 要加载bean定义的{@code BeanFactory}，形式为{@code BeanDefinitionRegistry}
	 */
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
		this(registry, true);
	}

	/**
	 * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory.
	 * <p>If the passed-in bean factory does not only implement the
	 * {@code BeanDefinitionRegistry} interface but also the {@code ResourceLoader}
	 * interface, it will be used as default {@code ResourceLoader} as well. This will
	 * usually be the case for {@link org.springframework.context.ApplicationContext}
	 * implementations.
	 * <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader}
	 * will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
	 * <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
	 * environment will be used by this reader.  Otherwise, the reader will initialize and
	 * use a {@link org.springframework.core.env.StandardEnvironment}. All
	 * {@code ApplicationContext} implementations are {@code EnvironmentCapable}, while
	 * normal {@code BeanFactory} implementations are not.
	 * @param registry the {@code BeanFactory} to load bean definitions into, in the form
	 * of a {@code BeanDefinitionRegistry}
	 * @param useDefaultFilters whether to include the default filters for the
	 * {@link org.springframework.stereotype.Component @Component},
	 * {@link org.springframework.stereotype.Repository @Repository},
	 * {@link org.springframework.stereotype.Service @Service}, and
	 * {@link org.springframework.stereotype.Controller @Controller} stereotype annotations
	 * @see #setResourceLoader
	 * @see #setEnvironment
	 */
	/**
	 * 为给定的bean工厂创建一个新的{@code ClassPathBeanDefinitionScanner}。
	 * <p>如果传入的bean工厂不仅实现了{@code BeanDefinitionRegistry}接口，
	 * 还实现了{@code ResourceLoader}接口，它也将被用作默认的{@code ResourceLoader}。
	 * 这通常是{@link org.springframework.context.ApplicationContext}实现的情况。
	 * <p>如果给定一个普通的{@code BeanDefinitionRegistry}，默认的{@code ResourceLoader}
	 * 将是{@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}。
	 * <p>如果传入的bean工厂还实现了{@link EnvironmentCapable}，其环境将被此读取器使用。
	 * 否则，读取器将初始化并使用{@link org.springframework.core.env.StandardEnvironment}。
	 * 所有{@code ApplicationContext}实现都是{@code EnvironmentCapable}，
	 * 而普通的{@code BeanFactory}实现则不是。
	 * @param registry 要加载bean定义的{@code BeanFactory}，形式为{@code BeanDefinitionRegistry}
	 * @param useDefaultFilters 是否包含默认过滤器用于
	 * {@link org.springframework.stereotype.Component @Component},
	 * {@link org.springframework.stereotype.Repository @Repository},
	 * {@link org.springframework.stereotype.Service @Service}, 和
	 * {@link org.springframework.stereotype.Controller @Controller} 注解
	 * @see #setResourceLoader
	 * @see #setEnvironment
	 */
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
		this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
	}

	/**
	 * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory and
	 * using the given {@link Environment} when evaluating bean definition profile metadata.
	 * <p>If the passed-in bean factory does not only implement the {@code
	 * BeanDefinitionRegistry} interface but also the {@link ResourceLoader} interface, it
	 * will be used as default {@code ResourceLoader} as well. This will usually be the
	 * case for {@link org.springframework.context.ApplicationContext} implementations.
	 * <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader}
	 * will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
	 * @param registry the {@code BeanFactory} to load bean definitions into, in the form
	 * of a {@code BeanDefinitionRegistry}
	 * @param useDefaultFilters whether to include the default filters for the
	 * {@link org.springframework.stereotype.Component @Component},
	 * {@link org.springframework.stereotype.Repository @Repository},
	 * {@link org.springframework.stereotype.Service @Service}, and
	 * {@link org.springframework.stereotype.Controller @Controller} stereotype annotations
	 * @param environment the Spring {@link Environment} to use when evaluating bean
	 * definition profile metadata
	 * @since 3.1
	 * @see #setResourceLoader
	 */
	/**
	 * 为给定的bean工厂创建一个新的{@code ClassPathBeanDefinitionScanner}，
	 * 并在评估bean定义配置文件元数据时使用给定的{@link Environment}。
	 * <p>如果传入的bean工厂不仅实现了{@code BeanDefinitionRegistry}接口，
	 * 还实现了{@link ResourceLoader}接口，它也将被用作默认的{@code ResourceLoader}。
	 * 这通常是{@link org.springframework.context.ApplicationContext}实现的情况。
	 * <p>如果给定一个普通的{@code BeanDefinitionRegistry}，默认的{@code ResourceLoader}
	 * 将是{@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}。
	 * @param registry 要加载bean定义的{@code BeanFactory}，形式为{@code BeanDefinitionRegistry}
	 * @param useDefaultFilters 是否包含默认过滤器用于
	 * {@link org.springframework.stereotype.Component @Component},
	 * {@link org.springframework.stereotype.Repository @Repository},
	 * {@link org.springframework.stereotype.Service @Service}, 和
	 * {@link org.springframework.stereotype.Controller @Controller} 注解
	 * @param environment 用于评估bean定义配置文件元数据的Spring {@link Environment}
	 * @since 3.1
	 * @see #setResourceLoader
	 */
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
										  Environment environment) {

		this(registry, useDefaultFilters, environment,
				(registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
	}

	/**
	 * 为给定的bean工厂创建一个新的{@code ClassPathBeanDefinitionScanner}，
	 * 并在评估bean定义配置文件元数据时使用给定的{@link Environment}。
	 * @param registry 要加载bean定义的{@code BeanFactory}，形式为{@code BeanDefinitionRegistry}
	 * @param useDefaultFilters 是否包含默认过滤器用于
	 * {@link org.springframework.stereotype.Component @Component},
	 * {@link org.springframework.stereotype.Repository @Repository},
	 * {@link org.springframework.stereotype.Service @Service}, 和
	 * {@link org.springframework.stereotype.Controller @Controller} 注解
	 * @param environment 用于评估bean定义配置文件元数据的Spring {@link Environment}
	 * @param resourceLoader 要使用的{@link ResourceLoader}
	 * @since 4.3.6
	 */
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
										  Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry不能为空");
		this.registry = registry;

		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		setEnvironment(environment);
		setResourceLoader(resourceLoader);
	}

	/**
	 * 返回此扫描器操作的BeanDefinitionRegistry。
	 */
	@Override
	public final BeanDefinitionRegistry getRegistry() {
		return this.registry;
	}

	/**
	 * 设置用于检测到的bean的默认值。
	 * @see BeanDefinitionDefaults
	 */
	public void setBeanDefinitionDefaults(@Nullable BeanDefinitionDefaults beanDefinitionDefaults) {
		this.beanDefinitionDefaults =
				(beanDefinitionDefaults != null ? beanDefinitionDefaults : new BeanDefinitionDefaults());
	}

	/**
	 * 返回用于检测到的bean的默认值(永远不会为{@code null})。
	 * @since 4.1
	 */
	public BeanDefinitionDefaults getBeanDefinitionDefaults() {
		return this.beanDefinitionDefaults;
	}

	/**
	 * 设置用于确定自动装配候选的名称匹配模式。
	 * @param autowireCandidatePatterns 要匹配的模式
	 */
	public void setAutowireCandidatePatterns(@Nullable String... autowireCandidatePatterns) {
		this.autowireCandidatePatterns = autowireCandidatePatterns;
	}

	/**
	 * 设置用于检测到的bean类的BeanNameGenerator。
	 * <p>默认是{@link AnnotationBeanNameGenerator}。
	 */
	public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
		this.beanNameGenerator =
				(beanNameGenerator != null ? beanNameGenerator : AnnotationBeanNameGenerator.INSTANCE);
	}

	/**
	 * 设置用于检测到的bean类的ScopeMetadataResolver。
	 * 注意这将覆盖任何自定义的"scopedProxyMode"设置。
	 * <p>默认是{@link AnnotationScopeMetadataResolver}。
	 * @see #setScopedProxyMode
	 */
	public void setScopeMetadataResolver(@Nullable ScopeMetadataResolver scopeMetadataResolver) {
		this.scopeMetadataResolver =
				(scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
	}

	/**
	 * 指定非单例作用域bean的代理行为。
	 * 注意这将覆盖任何自定义的"scopeMetadataResolver"设置。
	 * <p>默认是{@link ScopedProxyMode#NO}。
	 * @see #setScopeMetadataResolver
	 */
	public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
		this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(scopedProxyMode);
	}

	/**
	 * 指定是否注册注解配置后处理器。
	 * <p>默认是注册后处理器。关闭此选项可以忽略注解或以不同方式处理它们。
	 */
	public void setIncludeAnnotationConfig(boolean includeAnnotationConfig) {
		this.includeAnnotationConfig = includeAnnotationConfig;
	}

	/**
	 * 在指定的基础包中执行扫描。
	 * @param basePackages 要检查注解类的包
	 * @return 注册的bean数量
	 */
	public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);

		// 如果需要，注册注解配置处理器
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

	/**
	 * 在指定的基础包中执行扫描，返回注册的bean定义。
	 * <p>此方法<i>不</i>注册注解配置处理器，而是将此留给调用者。
	 * @param basePackages 要检查注解类的包
	 * @return 注册的bean集合(如果有)，用于工具注册目的(永远不会为{@code null})
	 */
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "必须至少指定一个基础包");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			// 核心方法 查找候选组件
 			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				// 解析作用域元数据
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				// 生成bean名称
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					// 后处理bean定义
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 处理通用定义注解
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					// 注册bean定义
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

	/**
	 * 对给定的bean定义应用进一步的设置，超出从扫描组件类中检索到的内容。
	 * @param beanDefinition 扫描到的bean定义
	 * @param beanName 给定bean的生成bean名称
	 */
	protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
		// 应用默认值
		beanDefinition.applyDefaults(this.beanDefinitionDefaults);
		if (this.autowireCandidatePatterns != null) {
			// 设置自动装配候选
			beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
		}
	}

	/**
	 * 使用给定的注册表注册指定的bean。
	 * <p>可以在子类中重写，例如调整注册过程或为每个扫描的bean注册更多的bean定义。
	 * @param definitionHolder bean定义加上bean名称
	 * @param registry 要注册bean的BeanDefinitionRegistry
	 */
	protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
	}

	/**
	 * 检查给定候选的bean名称，确定是否需要注册相应的bean定义或与现有定义冲突。
	 * @param beanName 建议的bean名称
	 * @param beanDefinition 相应的bean定义
	 * @return {@code true} 如果可以按原样注册bean；
	 * {@code false} 如果应该跳过，因为指定名称存在现有兼容的bean定义
	 * @throws ConflictingBeanDefinitionException 如果发现指定名称存在现有不兼容的bean定义
	 */
	protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
		if (!this.registry.containsBeanDefinition(beanName)) {
			return true;
		}
		BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
		BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
		if (originatingDef != null) {
			existingDef = originatingDef;
		}
		if (isCompatible(beanDefinition, existingDef)) {
			return false;
		}
		throw new ConflictingBeanDefinitionException("注解指定的bean名称'" + beanName +
				"'对于bean类[" + beanDefinition.getBeanClassName() + "]与现有的、不兼容的同名bean定义[" + existingDef.getBeanClassName() + "]冲突");
	}

	/**
	 * 确定给定的新bean定义是否与给定的现有bean定义兼容。
	 * <p>默认实现认为当现有bean定义来自相同源或来自非扫描源时它们是兼容的。
	 * @param newDefinition 新bean定义，源自扫描
	 * @param existingDefinition 现有bean定义，可能是显式定义的或先前从扫描生成的
	 * @return 定义是否被视为兼容，新定义将被跳过以支持现有定义
	 */
	protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
		return (!(existingDefinition instanceof ScannedGenericBeanDefinition) ||  // 显式注册的重写bean
				(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) ||  // 两次扫描相同的文件
				newDefinition.equals(existingDefinition));  // 两次扫描相同的类
	}

	/**
	 * 如果可能，从给定的注册表获取Environment，否则返回一个新的StandardEnvironment。
	 */
	private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
		Assert.notNull(registry, "BeanDefinitionRegistry不能为空");
		if (registry instanceof EnvironmentCapable) {
			return ((EnvironmentCapable) registry).getEnvironment();
		}
		return new StandardEnvironment();
	}
}